/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2014-2019.
 * Description: core function of kbox
 * Author: lichaoyue
 * Create: 2014-3-24
 */

#include "kbox_include.h"
#ifdef CONFIG_RTOS_EARLY_KBOX
#include <linux/console.h>
#include <linux/early_kbox_internal.h>
#endif
#include <linux/printk.h>
#include <linux/sched/clock.h>
#include "kbox_dynamic.h"

kbox_memory	kbox_memory_list;
kbox_device_cb	kbox_dev_cb;
kbox_region_cb *kbox_default_region;

#ifndef CONFIG_RTOS_KBOX_MINI
static void *dump_start;
static size_t dump_len;

char warnstr_dump[KBOX_WARNNING_LEN];
static char dump_region_name[KBOX_REGION_NAME_LEN];
static char *dump_path;
module_param(dump_path, charp, S_IRUSR | S_IWUSR | S_IRGRP);

#define KBOX_EXCEPTION_LOG_NAME_LEN 128

struct task_struct *kbox_daemon;
#endif
struct list_head kbox_region_list;

atomic_t dump_flag;

/*
 * Not supporting concurrent process for kbox, only one task can enter into the
 * kbox code area. We don't care of speed when doing most of the kbox operation.
 */
struct semaphore kbox_sem;

static unsigned short kbox_region_fd;

struct module *kbox_module;

/*
 * 0: default value
 * 1: compat mode adjust size
 */
int kbox_compat_value;

static kbox_region_desc *kbox_region_desc_find(const char *name);

static int kbox_do_create_region(const char *create_name, u32 create_size, unsigned char flags,
								kbox_region_desc **plogdes);

#ifndef CONFIG_RTOS_KBOX_MINI
static int kbox_exception_dump_valid(const void *viraddr, size_t length,
				char *region_name, char *full_dump_path)
{
	char *temp = NULL;
	int ret;

	if (dump_path == NULL) {
		error("%s abort for NULL dump_path!\n", __func__);
		return -EINVAL;
	}

	if (viraddr == NULL) {
		error("%s abort for NULL viraddr!\n", __func__);
		return -EINVAL;
	}

	if (length == 0) {
		error("%s abort for length = 0!\n", __func__);
		return -EINVAL;
	}

	temp = dump_path + strlen(dump_path) - 1;
	if (*temp == '\n')
		*temp = '\0';

	ret = snprintf(full_dump_path, KBOX_EXCEPTION_LOG_NAME_LEN,
			"%s/kbox_dump.region.%s", dump_path, region_name);
	if (ret < 0 || ret >= KBOX_EXCEPTION_LOG_NAME_LEN) {
		error("%s fail for snprintf get full dump_path: %s/kbox_dump.region.%s\n",
			__func__, dump_path, region_name);
		return -EINVAL;
	}
	return 0;
}

/* dump memory before clear it */
static int kbox_exception_dump(void *viraddr, size_t length, char *warnstr, char *region_name)
{
	struct file *fp = NULL;
	loff_t pos = 0;
	size_t warn_len;
	char full_dump_path[KBOX_EXCEPTION_LOG_NAME_LEN];
	struct inode *inode = NULL;

	int err_code = kbox_exception_dump_valid(viraddr, length, region_name,
						full_dump_path);
	if (err_code < 0)
		return err_code;

	fp = filp_open(full_dump_path, O_CREAT | O_RDWR, 0640);
	if (IS_ERR(fp))
		/* we can not remind it fails for we well do dump in kbox_write */
		return PTR_ERR(fp);

	inode = file_inode(fp);
	if (inode->i_nlink > 1) /* Hard link */
		goto out_close;
	if (!S_ISREG(inode->i_mode)) /* avoid write link file */
		goto out_close;

	kernel_write(fp, viraddr, length, &pos);
	warn_len = (warnstr != NULL) ? (strlen(warnstr)) : (0);
	if (warn_len > 0)
		kernel_write(fp, warnstr, warn_len, &pos);
out_close:
	filp_close(fp, NULL);
	return 0;
}

/*
 * The watchdog thread - runs every second and touches the timestamp.
 */
static int kbox_coredump(void *data)
{
	while (!kthread_should_stop()) {
		synchronize_rcu();
		if (!down_interruptible(&kbox_sem)) {
			if (dump_start != NULL && dump_len > 0) {
				(void)kbox_exception_dump(dump_start, dump_len, warnstr_dump, dump_region_name);
				atomic_dec(&dump_flag);
			}
			up(&kbox_sem);
		}
		kbox_control_resume();
		set_current_state(TASK_INTERRUPTIBLE);

		schedule();

		if (kthread_should_stop())
			break;
	}
	__set_current_state(TASK_RUNNING);

	return 0;
}

int wakeup_kboxd(void *start, unsigned long len, char *warnstr, char *region_name)
{
	/* kbox daemon is not created */
	if (kbox_daemon == NULL)
		return -EINVAL;
	if (dump_path == NULL || *dump_path != '/')
		return 0;

	/* right now we are on dumping */
	if (atomic_inc_return(&dump_flag) > 1) {
		atomic_dec(&dump_flag);
		return -EAGAIN;
	}

	dump_start = start;
	dump_len = len;

	if (warnstr != NULL) {
		warnstr_dump[KBOX_WARNNING_LEN - 1] = '\0';
		strncpy(warnstr_dump, warnstr, KBOX_WARNNING_LEN - 1);
	}
	strncpy(dump_region_name, region_name, KBOX_REGION_NAME_LEN);
	kbox_control_stop();
	wake_up_process(kbox_daemon);

	return 0;
}
#endif

/*
 * Description: generate an checksum for an buffer.
 * param	buff	pointer to an buffer for checksum.
 * param	size	the buffer length.
 * algo:
 *	convert the buffer into an char array.
 *	add all chars together.
 */
u32 kbox_checksum(u8 *buff, int size)
{
	u32 cksum = 0;

	if (buff == NULL || size < 0)
		return 0;

	while (size--)
		cksum += *buff++;

	return cksum;
}

void update_checksum(void)
{
	u32 checksum;

	checksum = kbox_checksum((u8 *)kbox_memory_list.region_table,
	   kbox_memory_list.region_table_len);
	kbox_memory_list.fs_head->file_desc_table_checksum = cpu_to_be32(checksum);

	kbox_memory_list.fs_head->basic.checksum = 0;
	checksum = kbox_checksum((u8 *)kbox_memory_list.fs_head,
	   sizeof(*(kbox_memory_list.fs_head)));
	kbox_memory_list.fs_head->basic.checksum = cpu_to_be32(checksum);
}

static int kbox_create_region_proc(kbox_region_cb *cb)
{
	cb->proc_entry =
		proc_create_data(cb->name, 0644, kbox_proc_regions, &kbox_proc_file_operations, cb);
	if (cb->proc_entry == NULL)
		return -ENOENT;

	proc_set_size(cb->proc_entry, cb->size);
	return 0;
}

static void kbox_remove_region_proc(kbox_region_cb *cb)
{
	if (cb == NULL)
		return;
	remove_proc_entry(cb->name, kbox_proc_regions);
}

/*
 * malloc an region cb and do basic initialization.
 * malloc an region cb
 * init basic fields
 * register proc entry for that region.
 */
kbox_region_cb *kbox_region_malloc(const char *name, u32 size, kbox_region_desc *log_des,
	struct module *module, bool is_history)
{
	int ret;
	u32 offset = get_desc_offset(log_des);
	/* alloc and init an region cb. */
	kbox_region_cb *cb = kzalloc(sizeof(kbox_region_cb), GFP_KERNEL);

	if (cb == NULL)
		goto out;

	ret = snprintf(cb->name, KBOX_REGION_NAME_LEN, "%s", name);
	if (ret >= KBOX_REGION_NAME_LEN) {
		error("%s: name %s too long, %zu > %d\n",
			__func__, name, strlen(name), KBOX_REGION_NAME_LEN);
		kfree(cb);
		return NULL;
	}
	spin_lock_init(&cb->logbuf_lock);
	cb->magic = KBOX_LOG_BUF_CB_MAGIC;

	if (offset < PAGE_SIZE) {
		notice("offset(0x%x) < PAGE_SIZE(0x%lx)", offset, PAGE_SIZE);
		set_desc_offset(log_des, PAGE_SIZE + kbox_default_reg_size);
	}
	cb->fd = kbox_region_fd++;
	if (kbox_region_fd == KBOX_NULL_REGION_ID)
		kbox_region_fd = 1;
	cb->size = get_desc_size(log_des);
	cb->log_buf = offset + kbox_memory_list.memory_head;
	cb->pregion_des = log_des;
	cb->module = module;
	cb->is_history = is_history;
	cb->is_newline = true;
	cb->fpos = 0;
	cb->part_copy = 0;
	cb->reset_fpos = false;
	atomic_set(&cb->proc_read_count, 0);
	if (module != NULL)
		snprintf(cb->module_name, KBOX_MODULE_NAME_LEN, "%s", module->name);
	else
		memset(cb->module_name, 0, KBOX_MODULE_NAME_LEN);

	if (kbox_create_region_proc(cb) < 0) {
		error("Proc entry creation for region %s failed\n", cb->name);
		kfree(cb);
		return NULL;
	}

	if (module != THIS_MODULE && !try_module_get(kbox_dev_cb.dev_desc->module)) {
		error("get module fail\n");
		kbox_remove_region_proc(cb);
		kfree(cb);
		cb = NULL;
	}
out:
	return cb;
}

/*
 * free an region cb
 *	unreg proc entry of that region
 *	free region cb.
 * no need to protect proc delete for filesystem will protect file would not delete when open it
 * and before free region control block, we must delete region proc file first. so wen read region, control block
 * wouldn't be null
 */
static void kbox_region_free(kbox_region_cb *cb)
{
	if (cb->module != THIS_MODULE)
		module_put(kbox_dev_cb.dev_desc->module);

	remove_proc_entry(cb->name, kbox_proc_regions);
	kfree(cb);
}

/*
 * find an region from region list.
 * param name  name of region.
 * return NULL:can not find that region; Not NULL:pointer to the found region cb
 */
kbox_region_cb *kbox_region_find_by_name(const char *name)
{
	struct list_head *ptr = NULL;
	kbox_region_cb *entry = NULL;
	kbox_region_cb *cb_found = NULL;

	list_for_each(ptr, &kbox_region_list) {
		entry = list_entry(ptr, kbox_region_cb, list);
		if (!strncmp(entry->name, name, KBOX_REGION_NAME_LEN)) {
			cb_found = entry;
			break;
		}
	}
	return cb_found;
}

#ifndef CONFIG_RTOS_KBOX_MINI
/*
 * find an region from region list.
 * param name  name of region.
 * return NULL:can not find that region;Not NULL:pointer to the found region cb
 */
kbox_region_cb *kbox_region_find(int rfd)
{
	struct list_head *ptr = NULL;
	kbox_region_cb *entry = NULL;
	kbox_region_cb *cb_found = NULL;

	list_for_each(ptr, &kbox_region_list) {
		entry = list_entry(ptr, kbox_region_cb, list);
		if (entry->fd == rfd) {
			cb_found = entry;
			break;
		}
	}
	return cb_found;
}
#endif

int kbox_get_region(int rfd, kbox_region_cb **pcb)
{
	int ret = 0;

	rcu_read_lock();
#ifndef CONFIG_RTOS_KBOX_MINI
	*pcb = kbox_region_find(rfd);
#else
	*pcb = kbox_default_region;
#endif
	if ((*pcb) == NULL) {
		rcu_read_unlock();
		ret = -EINVAL;
	}
	return ret;
}

void kbox_put_region(kbox_region_cb *cb)
{
	rcu_read_unlock();
}

/*
 * add an region into region  list.
 * param cb the pointer to kbox_region_cb
 * the nodes sort by node->start value.
 */
int kbox_region_insert(kbox_region_cb *cb)
{
	list_add_tail_rcu(&cb->list, &kbox_region_list);
	return 0;
}

static void dump_and_clean_region(kbox_region_cb *cb, unsigned int msg_len, const char *from)
{
	char warn_str[KBOX_WARNNING_LEN] = {0};
	snprintf(warn_str, KBOX_WARNNING_LEN, "[%s:%d]size:%u, start:%u, end:%u, region:%s, start_len=%u, from=%zu\n",
			__func__, __LINE__, get_desc_size(cb->pregion_des),
			get_desc_log_start(cb->pregion_des),
			get_desc_log_end(cb->pregion_des), cb->name, msg_len,
			from-cb->log_buf);
	zero_log_buf(cb, 1, warn_str);
}

static int get_one_record_len(kbox_region_cb *cb, const char *from, unsigned int *len)
{
	unsigned int one_record_len;
	int ret;
	if (cb->part_copy == 0) {
		one_record_len = *((unsigned int *)from);

		if (one_record_len == KBOX_LOGBUF_END_MAGIC)
			return KBOX_STATUS_FAIL;

		/* 4byte head and 4byte end = 8 */
		if (one_record_len == 0 ||
			one_record_len > (cb->size - MSG_HEAD_SIZE - MSG_END_SIZE)) {
			dump_and_clean_region(cb, one_record_len, from);
			return KBOX_STATUS_FAIL;
		}
		/* return record len size in ring buffer */
		ret = sizeof(unsigned int);
	} else {
		one_record_len = cb->part_copy;
		cb->part_copy = 0;
		/* record len isn't obtained from ring buffer, so return 0 */
		ret = 0;
	}

	*len = one_record_len;
	return ret;
}

static unsigned int read_ring_buffer_data(kbox_region_cb *cb, char *obj_buffer,
					  unsigned int obj_size,
					  unsigned int *rb_read_byte)
{
	unsigned int read_len = 0;
	unsigned int start_len;
	unsigned int total_len = 0;
	unsigned int tmp_len;
	int len_size_in_rb;
	char *from = cb->log_buf + (cb->fpos % cb->size);

	while (read_len < obj_size) {
		/* mes head is the last word or the last Byte */
		if (from >= cb->log_buf + cb->size)
			from = from - cb->size;

		len_size_in_rb = get_one_record_len(cb, from, &start_len);
		if (len_size_in_rb < 0)
			break;

		total_len += len_size_in_rb;
		from += len_size_in_rb;
		if (start_len > obj_size && read_len == 0) {
			cb->part_copy = start_len - obj_size;
			start_len = obj_size;
		}

		if ((start_len + read_len) > obj_size) {
			total_len -= sizeof(unsigned int);
			break;
		}

		/* mes head is the last word or the last Byte */
		if (from >= cb->log_buf + cb->size) {
			from = cb->log_buf;
		} else if (from + start_len > cb->log_buf + cb->size) {
			tmp_len = cb->log_buf + cb->size - from;
			memcpy(obj_buffer + read_len, from, tmp_len);
			read_len += tmp_len;
			start_len -= tmp_len;
			total_len += tmp_len;
			from = cb->log_buf;
		}
		memcpy(obj_buffer + read_len, from, start_len);

		from += ALIGN(start_len, MSG_ALIGN);
		read_len += (start_len);
		total_len += (ALIGN(start_len, MSG_ALIGN));

		if (cb->part_copy != 0)
			break;
	}
	*rb_read_byte = total_len;

	return read_len;
}
/*
 * read data from region.
 * param cb region cb.
 * param buffer_type 0:kernel buffer, 1:app buffer.
 *  if it's an app buffer, should use copy_to_user to copy data.
 * param buffer pointer to an buffer.
 * param buflen buffer length.
 * param fpos read position. should update it after read.
 * return <0 :error
 *    >= the length of read.
 */
int kbox_region_read(kbox_region_cb *cb, char __user *buffer, size_t buflen)
{
	unsigned int len;
	char *tmp_buff = NULL;
	unsigned int read_len;
	unsigned int total_len;
	unsigned int unread_len;
	unsigned long flags;

	if ((buflen == 0) || (buflen % MSG_ALIGN))
		return -EINVAL;

	if (buflen > cb->size)
		buflen = cb->size;

	tmp_buff = kmalloc(buflen, GFP_KERNEL);
	if (tmp_buff == NULL) {
		error("alloc tmp_buff fail\n");
		return -ENOMEM;
	}

	/* should lock the logbuf avoiding logbuf changed */
	kbox_printk_lock(&cb->logbuf_lock, &flags);
#ifndef CONFIG_RTOS_KBOX_MINI
	if (kbox_control_isstop()) {
		kbox_printk_unlock(&cb->logbuf_lock, &flags);
		warning("kbox is stopped\n");
		kfree(tmp_buff);
		return -EBUSY;
	}
#endif
	if (cb->reset_fpos == true) {
		cb->fpos = get_desc_log_start(cb->pregion_des);
		cb->part_copy = 0;
		cb->reset_fpos = false;
	}

	/*
	 * fops always be offset from log_start.
	 * if log_start or log_end changed when reading, may miss reading
	 * some data. We don't care this condition.
	 */
	/* 0----------------------------size
	 * |------end    start----fpos---|
	 * |--fpos------end    start-----|
	 * |start----fpos----end         |
	 */
	unread_len = get_desc_log_end(cb->pregion_des) - cb->fpos;
	len = (unread_len < buflen) ? unread_len : buflen;
	read_len = read_ring_buffer_data(cb, tmp_buff, len, &total_len);
	kbox_printk_unlock(&cb->logbuf_lock, &flags);

	if (copy_to_user(buffer, tmp_buff, read_len)) {
		kfree(tmp_buff);
		return -EFAULT;
	}

	cb->fpos += total_len;
	kfree(tmp_buff);
	return read_len;
}

/*
 * Function     : kbox_do_delete_region
 * Description  : delete region which region_desc refers to
 * Arg1         : kbox_region_desc *region_desc
 * Return       : NA
 */
static void kbox_do_delete_region(kbox_region_desc *region_desc)
{
	kbox_memory_list.fs_head->file_count--;
	kbox_memory_list.fs_head->basic.used_size -= get_desc_size(region_desc);
	memset(region_desc, 0, kbox_region_desc_size());
	update_checksum();
}

static void kbox_delete_region(kbox_region_desc *region_desc)
{
	if (kbox_memory_list.fs_head->basic.reserved1 == 0)
		kbox_do_delete_region(region_desc);
	else
		kbox_do_delete_dym_region(region_desc);
}

static bool check_if_region_exist_with_name(const char *create_name, u32 create_size,
	struct module *mod, kbox_region_cb **cb_ret, int *ret)
{
	kbox_region_cb *cb;
	cb = kbox_region_find_by_name(create_name);
	if (cb) {
		if (cb->size != create_size) {
			error("exist the same name region, registered difference size\n");
			*ret = -EEXIST;
		} else if (cb->is_history == FALSE && mod != NULL &&
				strncmp(cb->module_name, mod->name, KBOX_MODULE_NAME_LEN) != 0) {
			error("exist the same name region, but another module registered the region\n");
			*ret = -EEXIST;
		} else {
			/* after create region again, change history to new  */
			if (cb->is_history == TRUE && mod != NULL)
				snprintf(cb->module_name, KBOX_MODULE_NAME_LEN, "%s", mod->name);
			cb->is_history = FALSE;
			cb->module = mod;
		}
		*cb_ret = cb;

		return true;
	}
	return false;
}

static int kbox_create_region_action(const char *create_name, u32 create_size,
	struct module *mod, unsigned char ignor_size)
{
	kbox_region_cb *cb = NULL;
	int ret = 0;
	kbox_region_desc *kbox_logdes = NULL;

	if (strnlen(create_name, KBOX_REGION_NAME_LEN) == KBOX_REGION_NAME_LEN) {
		error("%s fail: no find \'\\0\' in region name\n", __func__);
		return -EINVAL;
	}

	if (create_size >= kbox_memory_list.memory_size || create_size == 0) {
		error("%s fail: create_size=%x  kbox.memory_size=%lx\n",
				__func__, create_size, kbox_memory_list.memory_size);
		return -EINVAL;
	}

	/* check if an region with the same name already exists */
	if (check_if_region_exist_with_name(create_name, create_size, mod, &cb, &ret))
		goto out;

	ret = kbox_do_create_region(create_name, create_size, (unsigned char)ignor_size, &kbox_logdes);
	if (ret < 0) {
		pr_info("\n");
		error("do_create_region fails!\n");
		goto out;
	}

	cb = kbox_region_malloc(create_name, create_size, kbox_logdes, mod, FALSE);
	if (cb == NULL) {
		if (ret == 1) /* the region is a new region, not a old region in dev_mem */
			kbox_delete_region(kbox_logdes);
		ret = -ENOMEM;
		goto out;
	}
	/* after registered, mark that the kernel region has data. */
	kbox_region_insert(cb);

out:
	return (ret < 0) ? ret : cb->fd;
}

/*
 * register an kernel region, which is logbuf type, for automatic flushing
 * param name name of the buffer.
 * param buf the pointer to buffer.
 * param len the buffer size.
 * return 0:OK; -1:fail.
 * the registered buf must be reordered first, than
 */
int kbox_create_region(struct kbox_region *reg)
{
	int ret = 0;

#ifndef CONFIG_RTOS_KBOX_MINI
	if (reg == NULL || !reg->size || reg->size != PAGE_ALIGN(reg->size) || reg->name[0] == '\0')
		return -EINVAL;

	ret = down_interruptible(&kbox_sem);
	if (ret < 0)
		/* -EINTR */
		return ret;

	/* insert dev first */
	if (kbox_dev_cb.dev_status != KBOX_DEV_STATUS_FS_OK) {
		pr_info("\n");
		error("--------------device is not OK--------------\n");
		ret = -ENODEV;
		goto out;
	}

	ret = kbox_create_region_action(reg->name, reg->size, reg->mod, KBOX_MARK_SIZE);

out:
	up(&kbox_sem);

#endif
	return ret;
}
EXPORT_SYMBOL(kbox_create_region);

int kbox_region_forbidden_record_time(int rfd)
{
	kbox_region_cb *cb = NULL;
	int ret;

	ret = kbox_get_region(rfd, &cb);
	if (ret != 0)
		return -EINVAL;

	cb->forbidden_record_time = true;
	kbox_put_region(cb);

	return 0;
}
EXPORT_SYMBOL_NS(kbox_region_forbidden_record_time, RTOS_DEBUG_KSNAPSHOT);

unsigned int kbox_get_region_size(const char *region_name)
{
	kbox_region_cb *cb;

	if (!region_name)
		return 0;

	cb = kbox_region_find_by_name(region_name);

	return (cb) ? (cb->size) : (0);
}
EXPORT_SYMBOL(kbox_get_region_size);

int kbox_create_default_reg(void)
{
	int ret;
	kbox_region_cb *entry = NULL;
	unsigned int max_region_size = kbox_memory_list.memory_size
		- kbox_memory_list.fs_head->basic.data_offset;

	kbox_default_region = NULL;
#ifdef CONFIG_RTOS_KBOX_MINI
	kbox_default_reg_size = max_region_size;
#endif
	if (!kbox_default_reg_size ||
	    PAGE_ALIGN(kbox_default_reg_size) > max_region_size) {
		warning("Default region size can't be 0! or > kbox free mem size");
		warning("Auto reset kbox default region size\n");
		kbox_default_reg_size = min((unsigned int)KBOX_DEFAULT_REG_SIZE,
					    (unsigned int)max_region_size / 2);
	}
	/* KBOX_IGNORE_SIZE, no check region size in kbox devmem */
	ret = kbox_create_region_action(KBOX_DEFAULT_REG_NAME, PAGE_ALIGN(kbox_default_reg_size),
		THIS_MODULE, KBOX_IGNORE_SIZE);
	if (ret < 0) {
		error("register default region fail, is it possible?\n");
		goto out;
	}
	entry = kbox_region_find_by_name(KBOX_DEFAULT_REG_NAME);
	if (entry == NULL) {
		error("kbox_unregister_region: fail\n");
		goto out;
	}

	entry->fd = 0;
	kbox_default_region = entry;

out:
	return ret;
}

/*
 * find an region in an kbox device.
 * param	name	the name of the region.
 * param	cb		the pointer to kbox device cb.
 */
static kbox_region_desc *kbox_region_desc_find(const char *name)
{
	kbox_region_desc *region = NULL;
	int i = 0;

	if (name == NULL || kbox_memory_list.fs_head->file_count > kbox_max_region_num)
		return NULL;

	/* traverse the file table on device. */
	for (i = 0; i < kbox_memory_list.fs_head->file_count; i++) {
		region = get_region_desc(i);
		if (!strncmp(name, region->name, KBOX_REGION_NAME_LEN))
			return region;
	}

	return kbox_dym_region_desc_find(name);
}

/* find an empty region desc slot in an kbox device. */
static kbox_region_desc *kbox_region_desc_empty_find(void)
{
	u16 file_count = kbox_memory_list.fs_head->file_count;

	if (file_count >= kbox_memory_list.fs_head->file_support) {
		error("No enough kbox regoin\n");
		return NULL;
	}
	return get_region_desc(file_count);
}

static char *destroyed_kbox_mem_head;

/* save kbox mem head for debug kbox mem destroy when kbox init */
static void save_destroyed_kbox_mem_head(void)
{
	if (destroyed_kbox_mem_head != NULL) /* avoid reentry */
		return;

	destroyed_kbox_mem_head = kmalloc(PAGE_SIZE, GFP_KERNEL);
	if (destroyed_kbox_mem_head == NULL)
		return;

	memcpy(destroyed_kbox_mem_head, kbox_memory_list.memory_head, PAGE_SIZE);
}

#ifndef CONFIG_RTOS_KBOX_MINI

/*
 * xxxxxxxx: xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx  xxxxxxxxxxxxxxxx\n
 * size = (PAGE_SIZE / 16) * (4*2 + 2 + 16 * 2 + 16/4 + 1 + 1 + 16) + start + end
 * = 4 * PAGE_SIZE + start + end;
 * < 4 * PAGE_SIZE + PAGE_SIZE / 4
 */
#define KBOX_TMP_BUF_SIZE ((4 * PAGE_SIZE) + (PAGE_SIZE / 4))
#define END_INFO_SIZE (PAGE_SIZE / 8)
#define DUMP_ROW_SIZE 16
#define DUMP_GROUP_SIZE 4
static int dump_destroyed_kbox_mem_head(char *buf)
{
	int line_len;
	unsigned int i;
	int ret_size = 0;

	line_len = snprintf(buf, KBOX_TMP_BUF_SIZE, "-----start %s----\n", __func__);
	if (line_len <= 0 || line_len > KBOX_TMP_BUF_SIZE)
		return ret_size;
	ret_size += line_len;

	for (i = 0; i < PAGE_SIZE; i += DUMP_ROW_SIZE) {
		ret_size += hex_dump_to_buffer((char *)&i, sizeof(unsigned int),
						   DUMP_ROW_SIZE, DUMP_GROUP_SIZE,
						   buf + ret_size, KBOX_TMP_BUF_SIZE - ret_size,
						   false);
		*(buf + ret_size) = ':';
		*(buf + ret_size + 1) = ' ';

		/* plus 2: a semicolon and a space  */
		ret_size += 2;
		ret_size += hex_dump_to_buffer(destroyed_kbox_mem_head + i,
						   DUMP_ROW_SIZE, DUMP_ROW_SIZE,
						   DUMP_GROUP_SIZE, buf + ret_size,
						   KBOX_TMP_BUF_SIZE - ret_size, true);
		*(buf + ret_size) = '\n';
		ret_size++;
		if (ret_size > KBOX_TMP_BUF_SIZE - END_INFO_SIZE) /* END_INFO_SIZE for save end info */
			break;
	}
	line_len = snprintf(buf + ret_size, KBOX_TMP_BUF_SIZE - ret_size, "-----end %s----\n", __func__);
	if (line_len <= 0 || line_len > KBOX_TMP_BUF_SIZE - ret_size)
		return ret_size;
	ret_size += line_len;

	return ret_size;
}

static int show_destroyed_kbox_mem_head(void *ignore)
{
	int ret = 0;
	char *buf = NULL;
	int len = 0;

	if (unlikely(destroyed_kbox_mem_head == NULL))
		return -EINVAL;
	buf = kmalloc(KBOX_TMP_BUF_SIZE, GFP_KERNEL);
	if (buf == NULL) {
		error("kbox mem head is destroyed, %pK, but fail to show it when kmalloc\n", destroyed_kbox_mem_head);
		ret = -ENOMEM;
		goto free_destroyed_kbox_mem_head;
	}

	len = dump_destroyed_kbox_mem_head(buf);
	kbox_write(KBOX_DEFAULT_REG_ID, buf, len);
	kfree(buf);
free_destroyed_kbox_mem_head:
	kfree(destroyed_kbox_mem_head);
	destroyed_kbox_mem_head = NULL;
	return ret;
}
#undef KBOX_TMP_BUF_SIZE
#undef END_INFO_SIZE
#undef DUMP_ROW_SIZE
#undef DUMP_GROUP_SIZE
#endif

/*
 * format a kbox device.
 * return = 0, Ok; <0, error.
 *	(1)kbox must be in maintain status.
 *	(2)free old region desc table and alloc an new one.
 *	(3)init fs_head and region desc table
 * flushing the superblock into device will be done when mounting
 * the device.
 */
static void kbox_do_format_dev(void)
{
	int tmp_len = 0;
	kbox_fs_head *fs_head = kbox_memory_list.fs_head;
	kbox_region_desc *region_table = NULL;

	/* clean other fields of dev cb */
	tmp_len = kbox_max_region_num * kbox_region_desc_size();

	/* init fs_head */
	memset(fs_head, 0, sizeof(*fs_head));
	fs_head->basic.magic_num = cpu_to_be32(KBOX_FS_TYPE_KBOX_MAGIC);
	fs_head->basic.version = KBOX_FS_TYPE_KBOX_VER1;

	fs_head->basic.byteorder = KBOX_BYTEORDER;
	fs_head->file_support = kbox_max_region_num;
	fs_head->file_desc_size = kbox_region_desc_size();
	fs_head->file_count = 0;
	/* file_desc_table_offset is aligned to 64bit */
	fs_head->file_desc_table_offset = (sizeof(*fs_head) + 7) & (~7);

	fs_head->basic.data_offset = PAGE_ALIGN(fs_head->file_desc_table_offset + (unsigned int)tmp_len);
	fs_head->basic.used_size = fs_head->basic.data_offset;

	/* make the new region table. */
	region_table = (kbox_region_desc *)((char *)kbox_memory_list.fs_head + fs_head->file_desc_table_offset);

	/* init region desc table */
	memset(region_table, 0, tmp_len);

	kbox_memory_list.region_table = region_table;
	kbox_memory_list.region_table_len = tmp_len;
	update_checksum();
}

static int check_region_desc_same(kbox_region_desc *region_desc, u32 create_size, unsigned char flags)
{
#ifndef CONFIG_RTOS_KBOX_MINI
	u32 offset = get_desc_offset(region_desc);
#endif
	u32 size = get_desc_size(region_desc);

	/* we find the region in the device */
	if (create_size != size && !(flags && KBOX_IGNORE_SIZE)) {
		error("create region %s in dev, but wrong size %d != %d\n",
			region_desc->name, create_size, size);
		return -EINVAL;
	}
#ifndef CONFIG_RTOS_KBOX_MINI
	if (offset + size < offset) {
		error("data over flow offset %u, size %u\n", offset, size);
		memset(region_desc, 0, kbox_region_desc_size());
		return -ENOSPC;
	}

	if (offset + size > kbox_memory_list.memory_size) {
		error("the context of regions behind %s is lost, maybe ur device size decreased\n",
			region_desc->name);
		kbox_memory_list.fs_head->basic.used_size = offset;
		kbox_memory_list.fs_head->file_count = region_desc->index - 1;
		memset(region_desc, 0,
			   kbox_region_desc_size() * (kbox_max_region_num - region_desc->index + 1));
		update_checksum();
		return -ENOSPC;
	}
#endif
	return 0;
}

static int create_new_kbox_region(kbox_region_desc **plogdes,
				  const char *create_name, u32 create_size, unsigned char flags)
{
	kbox_region_desc *region_desc = NULL;
	u32 rem_size = kbox_memory_list.memory_size - kbox_memory_list.fs_head->basic.used_size;
	if (create_size > rem_size) {
		error("create region: Out of memory!\n");
		return -ENOMEM;
	}

	/* find an empty region desc slot */
	region_desc = kbox_region_desc_empty_find();
	if (region_desc == NULL) {
		error("create region: the base region desc slot is full.\n");
		return -ENOENT;
	}

	kbox_memory_list.fs_head->file_count++;

	/* fill region desc */
	snprintf(region_desc->name, KBOX_REGION_NAME_LEN, "%s", create_name);
	set_desc_size(region_desc, PAGE_ALIGN(create_size));
	set_desc_offset(region_desc, kbox_memory_list.fs_head->basic.used_size);
	region_desc->index = kbox_memory_list.fs_head->file_count;
	set_desc_log_start(region_desc, 0);
	set_desc_log_end(region_desc, 0);
	set_desc_real_size(region_desc, 0);
	region_desc->flags = flags;

	*plogdes = region_desc;

	kbox_memory_list.fs_head->basic.used_size += get_desc_size(region_desc);
	update_checksum();
	return 1; /* add a new region ok */
}
/*
 * create a region on an kbox device.
 * param	create_name	the name of region
 * param	create_sie	the size of region
 * return value
 *	= 0, Ok, this is a old region
 *	= 1, OK, we create a new region
 *	< 0, error.
 *	find an empty region desc entry on dev.
 *	find an data section for that region.
 *	update superblock
 */
static int kbox_do_create_region(const char *create_name, u32 create_size, unsigned char flags,
								kbox_region_desc **plogdes)
{
	kbox_region_desc *region_desc = NULL;
	int ret = 0;

	notice("kbox:create region %s, size=%d\n", create_name, create_size);
	if (kbox_memory_list.fs_head->basic.magic_num != cpu_to_be32(KBOX_FS_TYPE_KBOX_MAGIC)) {
#ifndef CONFIG_RTOS_KBOX_MINI
		(void)kbox_exception_dump(kbox_memory_list.memory_head, (size_t)kbox_memory_list.memory_size,
				"checked magicnum wrong when create region", "whole_kbox_mem");
#endif
		*plogdes = NULL;
		ret = -EBFONT;
		return ret;
	}

	/* kernel region: search on current device only.A kernel region can be created on multiple device */
	region_desc = kbox_region_desc_find(create_name);
	if (region_desc) {
		ret = check_region_desc_same(region_desc, create_size, flags);
		if (ret == 0)
			*plogdes = region_desc;
		return ret;
	}

	ret = create_new_kbox_region(plogdes, create_name, create_size, flags);
	if (ret == -ENOENT)
		return create_new_kbox_dym_region(plogdes, create_name, create_size, flags);

	return ret;
}

/*
 * free kbox_region_list
 * call by kbox_erase_regions and kbox_unregister_device
 */
static void kbox_region_list_free(void)
{
	kbox_region_cb *entry = NULL;

	while (!list_empty(&kbox_region_list)) {
		entry = list_first_entry(&kbox_region_list, kbox_region_cb, list);
		if (entry == NULL) {
			error("kbox_unregister_region:fail\n");
			break;
		}
		notice("kbox_unregister_region: %s, cb=%pK\n", entry->name, entry);

		list_del_rcu(&entry->list);
		synchronize_rcu();
		kbox_region_free(entry);
	}
}

/*
 * Function     :  erase all regions
 * Description  :  free kbox_region_list and format kbox dev mem
 * Arg1         :  NA
 */
void kbox_erase_regions(void)
{
	notice("erase regions start\n");
	/* destroy the cb */
	kbox_region_list_free();

	kbox_do_format_dev();
	notice("erase regions end\n");
}

int create_region_desc(kbox_region_desc *region)
{
	u32 size = get_desc_size(region);
	kbox_region_cb *cb =
		kbox_region_malloc(region->name, size, region, NULL, TRUE);
	if (cb == NULL) {
		error("Out of memory when create history region :%s size:%u\n",
		      region->name, size);
		return -ENOMEM;
	}
	kbox_region_insert(cb);
	return 0;
}

/*
 * create histoiry regions
 * for  display all regions after kbox init, before real ko create regions
 * used by read kbox regions info after kernel reboot
 */
static int create_history_regions(void)
{
	kbox_region_desc *region;
	int i;
	int ret;

	for (i = 0; i < kbox_memory_list.fs_head->file_count; i++) {
		region = get_region_desc(i);

		/*
		 * panic region will created by kbox_create_default_reg()
		 *  if create panic region here,
		 *  will cause kbox_create_default_reg fail when reboot
		 *  after change panic region size
		 */
		if (strncmp(region->name, KBOX_DEFAULT_REG_NAME, sizeof(KBOX_DEFAULT_REG_NAME)) == 0)
			continue;

		ret = create_region_desc(region);
		if (ret != 0)
			return ret;
	}

	return 0;
}

static bool check_fs_head(const char *name, char *warning_str, int *warning_str_len)
{
	u32 checksum_tmp, checksum;

	/* check fs_head */
	if (cpu_to_be32(KBOX_FS_TYPE_KBOX_MAGIC) != kbox_memory_list.fs_head->basic.magic_num) {
		error("%s: fs magic not match. It's ok if first time using this device\n",
			name);
#ifndef CONFIG_RTOS_KBOX_MINI
		*warning_str_len = snprintf(warning_str, KBOX_WARNNING_LEN,
			"kbox fs magic not match\n");
#endif
		return true;
	}

	checksum_tmp = kbox_memory_list.fs_head->basic.checksum;
	/* we must set chechsum value 0 first for we can not add chechsum value to calculate it */
	kbox_memory_list.fs_head->basic.checksum = 0;
	checksum = kbox_checksum((u8 *)kbox_memory_list.fs_head, sizeof(*(kbox_memory_list.fs_head)));
	checksum = cpu_to_be32(checksum);
	if (checksum != checksum_tmp) {
		error("%s: checksum fs head fail, format device\n", name);
#ifndef CONFIG_RTOS_KBOX_MINI
		*warning_str_len = snprintf(warning_str, KBOX_WARNNING_LEN,
			"kbox checksum fs head fail\n");
#endif
		return true;
	}
	kbox_memory_list.fs_head->basic.checksum = checksum;

	/* now we just support kbox_max_region_num regions */
	if (kbox_memory_list.fs_head->file_support != kbox_max_region_num) {
#ifndef CONFIG_RTOS_KBOX_MINI
		*warning_str_len = snprintf(warning_str, KBOX_WARNNING_LEN,
			"kbox file_support num error\n");
#endif
		return true;
	}

	if (kbox_memory_list.fs_head->file_count > kbox_memory_list.fs_head->file_support ||
		(kbox_memory_list.fs_head->basic.used_size & (PAGE_SIZE - 1)) ||
		kbox_memory_list.fs_head->basic.used_size > kbox_memory_list.memory_size)
		return true;

	return false;
}

bool check_kbox_region_desc_single(kbox_region_desc *region, u32 data_offset,
				   char *warning_str, int *warning_str_len)
{
	u32 offset, size, log_start, log_end;

	offset = get_desc_offset(region);
	size = get_desc_size(region);
	log_start = get_desc_log_start(region);
	log_end = get_desc_log_end(region);

	if (data_offset != offset) {
#ifndef CONFIG_RTOS_KBOX_MINI
		*warning_str_len = snprintf(warning_str, KBOX_WARNNING_LEN,
			"region(%s): data_offset_tmp(0x%x) is not equal data_offset(0x%x)\n",
			region->name, data_offset, offset);
#endif
		return true;
	}

	if (log_start >= size) {
#ifndef CONFIG_RTOS_KBOX_MINI
		*warning_str_len = snprintf(warning_str, KBOX_WARNNING_LEN,
			"%s 's start(%u)larger than size(%u)\n", region->name, log_start, size);
#endif
		return true;
	}

	if ((log_end - log_start)  > (size - 4)) {
		/* 4 means A5A5A5A5 at the messages end */
#ifndef CONFIG_RTOS_KBOX_MINI
		*warning_str_len = snprintf(warning_str, KBOX_WARNNING_LEN,
			"%s 's space between start(%u) and end (%u) is larger than size(%u)\n",
			region->name, log_start, log_end, size);
#endif
		return true;
	}
	/* support dym region may cause index overflow, no index verification */
	if (kbox_memory_list.fs_head->basic.reserved1 == 0 && region->index < 1) {
#ifndef CONFIG_RTOS_KBOX_MINI
		*warning_str_len = snprintf(warning_str, KBOX_WARNNING_LEN,
			"%s 's index(%u) error", region->name, region->index);
#endif
		return true;
	}
	return false;
}

static bool check_kbox_region_desc(char *warning_str, int *warning_str_len)
{
	u32 data_offset_tmp;
	kbox_region_desc *region = NULL;
	u32 i;
	u32 region_desc_size = kbox_region_desc_size();

	/* alloc memory for file desc table */
	kbox_memory_list.region_table = (kbox_region_desc *)((char *)kbox_memory_list.fs_head +
		kbox_memory_list.fs_head->file_desc_table_offset);

	/* should set the region table length, or the checksum of fs_head will be 0 */
	kbox_memory_list.region_table_len = kbox_max_region_num * region_desc_size;

	/* checksum file desc table */
	data_offset_tmp = kbox_memory_list.fs_head->basic.data_offset;
	region = kbox_memory_list.region_table;
	for (i = 0; i < kbox_memory_list.fs_head->file_count; i++) {
		if (check_kbox_region_desc_single(region, data_offset_tmp, warning_str, warning_str_len))
			return true;

		data_offset_tmp += get_desc_size(region);
		region = (kbox_region_desc *)((char *)region + region_desc_size);
	}

	if (data_offset_tmp > kbox_memory_list.memory_size) {
#ifndef CONFIG_RTOS_KBOX_MINI
		*warning_str_len = snprintf(warning_str, KBOX_WARNNING_LEN,
			"%s 's offset(%u), size(%u), memory size(%lu)\n",
			region->name, get_desc_offset(region), get_desc_size(region),
			kbox_memory_list.memory_size);
#endif
		return true;
	}

	return false;
}

/*
 * check if the device needs to be formatted before registering to kbox core
 * called in kbox_register_device()
 * @param name the name of the kbox device's description structure.
 */
static bool check_needs_formatting_device(const char *name, char *warning_str, int *warning_str_len)
{
	if (check_fs_head(name, warning_str, warning_str_len))
		return true;

	if (check_kbox_region_desc(warning_str, warning_str_len))
		return true;
	return check_kbox_dym_region_desc(warning_str, warning_str_len);
}

static void write_kbox_restart_log(void)
{
#define MAX_BUF_SIZE	64
	char tbuf[MAX_BUF_SIZE];
	int tlen;
	unsigned long long t;
	unsigned long nanosec_rem;

	t = cpu_clock(0);
	nanosec_rem = do_div(t, NSEC_PER_SEC);
	tlen = snprintf(tbuf, MAX_BUF_SIZE, "------------------kbox restart:[%5lu.%06lu]----------------\n",
			(unsigned long) t,
			nanosec_rem / NSEC_PER_USEC);
	/*
	 * snprintf return the len of input strings, although
	 * the current return value does not exceed MAX_BUF_SIZE,
	 * we still add the return value check.
	 */
	if (tlen > MAX_BUF_SIZE)
		tlen = MAX_BUF_SIZE;
	(void)kbox_write(KBOX_DEFAULT_REG_ID, tbuf, (unsigned int)tlen);
#undef MAX_BUF_SIZE
}

static int init_kbox_memory_list(struct kbox_dev_des *dev_desc)
{
	kbox_memory_list.memory_head = dev_desc->map_op.mapped_addr;
	if (kbox_memory_list.memory_head == NULL)
		return -ENOMEM;

	kbox_memory_list.memory_size = dev_desc->size;
	kbox_memory_list.fs_head = (kbox_fs_head *)kbox_memory_list.memory_head;
	kbox_memory_list_dym_init();
	/* load supperblock */
	kbox_dev_cb.dev_status = KBOX_DEV_STATUS_NOT_FORMAT; /* default value */
	return 0;
}

static bool create_kbox_regions(void)
{
	/* panic region id is 0, need create it before other region */
	if (kbox_create_default_reg() < 0)
		return false;
	/*
	* panic region create by kbox_create_default_reg,
	* no need create panic in create_history_regions
	*/
	if (create_history_regions() < 0)
		return false;

	if (create_dym_history_regions() < 0)
		return false;

	return true;
}

#ifndef CONFIG_RTOS_KBOX_MINI
static void check_and_format_kbox_data(const char *name)
{
	bool is_create_successfully;
	char warning_str[KBOX_WARNNING_LEN] = {0};
	/*
	 * No formatting will not modify the value of the variable,
	 * so it must be initialized, otherwise a random value will
	 * be obtained from the stack.
	 */
	int warning_str_len = 0;
	if (!check_needs_formatting_device(name, warning_str, &warning_str_len))
		goto REUSE_KBOX;

	do {
		(void)kbox_exception_dump(kbox_memory_list.memory_head,
					  (size_t)kbox_memory_list.memory_size,
					  warning_str, "whole_kbox_mem");
		save_destroyed_kbox_mem_head();
		kbox_do_format_dev();
REUSE_KBOX:
		is_create_successfully = create_kbox_regions();
	} while (!is_create_successfully);

#ifdef CONFIG_RTOS_EARLY_KBOX
	/* set null of console hook, for when kbox start, early kbox disable */
	register_nolevel_console(NULL);
#endif
	kbox_dev_cb.dev_status = KBOX_DEV_STATUS_FS_OK;
	write_kbox_restart_log();
	if (warning_str_len > 0) {
		(void)kbox_write(KBOX_DEFAULT_REG_ID, warning_str, warning_str_len);
		notice("kbox mem maybe destroyed, format it\n");
	}
	if (destroyed_kbox_mem_head != NULL)
		kthread_run(show_destroyed_kbox_mem_head, NULL, "show_destroyed");
}
#else
static void check_and_format_kbox_data(const char *name)
{
	bool is_create_successfully;

	if (!check_needs_formatting_device(name, NULL, NULL))
		goto REUSE_KBOX;

	do {
		save_destroyed_kbox_mem_head();
		kbox_do_format_dev();
REUSE_KBOX:
		is_create_successfully = create_kbox_regions();
	} while (!is_create_successfully);

#ifdef CONFIG_RTOS_EARLY_KBOX
	/* set null of console hook, for when kbox start, early kbox disable */
	register_nolevel_console(NULL);
#endif
	kbox_dev_cb.dev_status = KBOX_DEV_STATUS_FS_OK;
	write_kbox_restart_log();
}
#endif

/*
 * brief register an kbox device to kbox core
 * param   dev_desc  the pointer to kbox device's description structure.
 * return  0: OK; -1: fail.
 *	(1)alloc an dev_cb;
 *	(2)load superblock
 *	(3)fsck superblock
 *	(4)add to dev cb list.create proc entry for this dev
 */
int kbox_register_device(struct kbox_dev_des *dev_desc)
{
	int ret = 0;

	if (dev_desc == NULL || dev_desc->size < PAGE_SIZE ||
		dev_desc->device_name[0] == '\0')
		return -EINVAL;

	ret = down_interruptible(&kbox_sem);
	if (ret < 0)
		/* -EINTR */
		return ret;

	if (kbox_dev_cb.dev_status == KBOX_DEV_STATUS_FS_OK) {
		pr_info("\n");
		error("---------------had device--------------\n");
		up(&kbox_sem);
		return -EBUSY;
	}

	kbox_dev_cb.dev_desc = dev_desc;

	ret = init_kbox_memory_list(dev_desc);
	if (ret < 0) {
		kbox_region_list_free();
		kbox_dev_cb.dev_desc = NULL;
	} else {
		check_and_format_kbox_data(dev_desc->device_name);
	}
	up(&kbox_sem);

	return ret;
}

#ifndef CONFIG_RTOS_KBOX_MINI
/*
 * unregister an kbox device
 * param	name	name of the kbox device.
 *	(1)must be in maintain mode.
 *	(2)unreg proc entry
 *	(3)free dev cb.
 */
int kbox_unregister_device(const char *name)
{
	int ret = 0;

	if (name == NULL || *name == '\0') {
		error("device name should not be \"\"\n");
		return -EINVAL;
	}

	ret = down_interruptible(&kbox_sem);
	if (ret < 0) /* -EINTR */
		return ret;
	if (kbox_dev_cb.dev_desc == NULL ||
		strncmp(kbox_dev_cb.dev_desc->device_name, name,
			KBOX_DEV_NAME_MAXLEN)) {
		up(&kbox_sem);
		return -EINVAL;
	}

	/* destroy the cb */
	kbox_region_list_free();

	kbox_memory_list.memory_head = NULL;
	kbox_memory_list.memory_size = 0;

	kbox_region_fd = 0;

	memset(&kbox_dev_cb, 0, sizeof(kbox_dev_cb));
	dump_start = NULL;
	dump_len = 0;

	up(&kbox_sem);
	return ret;
}

#endif

/*
 * initialize kbox core module
 * return	0:OK; -1:fail
 */
int kbox_core_init(void)
{
	INIT_LIST_HEAD(&kbox_region_list);

#ifndef CONFIG_RTOS_KBOX_VIRTUAL
	if (!kbox_default_reg_size || kbox_default_reg_size != PAGE_ALIGN(kbox_default_reg_size))
		return -EINVAL;
#endif
	sema_init(&kbox_sem, 1);

	/* init kbox device struct */
	memset(&kbox_dev_cb, 0, sizeof(kbox_dev_cb));
	kbox_module = THIS_MODULE;
#ifndef CONFIG_RTOS_KBOX_MINI
	kbox_daemon = kthread_create(kbox_coredump, NULL, "kboxd");
	if (IS_ERR(kbox_daemon)) {
		error("kbox init create kthread kbox_coredump failed!\n");
		kbox_daemon = NULL;
		goto out;
	}
	wake_up_process(kbox_daemon);
out:
#endif
	return 0;
}

/*
 * finalize kbox core module
 * return  0:OK; -1:fail
 */
int kbox_core_fini(void)
{
#ifndef CONFIG_RTOS_KBOX_MINI
	if (kbox_daemon != NULL)
		kthread_stop(kbox_daemon);

	if (dump_start != NULL) {
		vfree(dump_start);
		dump_start = NULL;
	}
#endif
	return 0;
}

